home *** CD-ROM | disk | FTP | other *** search
/ The Best of MacTutor - S…e Code for Volumes 1 to 5 / The Best of MacTutor - Source Code for Volume 1-5 (Wayzata Technology)(6031)(1990).bin / Source Code / #50 (Nov 89) / SC#50.sit / Source Code Dispatcher / dispatcher.p < prev    next >
Text File  |  1989-07-13  |  31KB  |  875 lines

  1. {
  2.  File:      dispatcher.p
  3.  Program:   dispatcher
  4.  Author:    Roger A. Horton
  5.               Verity Software Systems
  6.               Copyright 1989, All Rights Reserved
  7.  Creation:  6/4/89
  8.  Purpose:   Create a small utility program to launch applications
  9.               from the Finder with associated launch documents. This
  10.               is especially useful for custom applications that require
  11.               a number of files, and that may cause a user confusion in
  12.               installing and launching the application. 
  13.  Technique: This program performs a launch (not a sublaunch) using the
  14.               code of Mac Tech Note #126 as a model. The utility does not
  15.               directly initiate the launch, but relies on associated
  16.               data documents to initiate the process, and to determine
  17.               which application and documents are to be launched. The 
  18.               launch utility and document(s) can be located anywhere in 
  19.               the file system. By double clicking on a launch document, 
  20.               the user causes the Finder to locate and launch the launch
  21.               utility program. The utility then reads the launch data 
  22.               document to obtain the name and location of the application
  23.               and its associated document. The Finder application parameters
  24.               are then set up, and the application is launched. If the 
  25.               location of the application or its associated documents has
  26.               changed since the last launch, the user is prompted to find
  27.               their new location. In essence, the launch utility documents
  28.               act as aliases for target applications, and add additional
  29.               usefulness by providing for automatic control of associated
  30.               application documents.
  31.    Program History:
  32. }
  33.  
  34. program scan;
  35.  
  36. {$D+}               { Macsbug/TMON symbols    }
  37. {$R-}               { Turn off range checking }
  38.  
  39. uses
  40.   { Macintosh toolbox units } 
  41.   PasLibIntf,Memtypes,QuickDraw,OSIntf,ToolIntf,PackIntf,
  42.   SANE, PrintTraps, ROMDefs
  43.   { Custom units }
  44.  
  45. const
  46.   active        =     0;    { for use in controls and menus }
  47.   inactive      =   255;    
  48.  
  49.   DfltDlogID    =   200;    { resource ID for configuration dialog }
  50.   StopDlogID    =   201;    { resource ID for stop alert dialog }
  51.   
  52.   AppParmGVar    =   $AEC;   { AppParmHandle system global variable }
  53.  
  54. type
  55.   { general types }
  56.   pLaunchStruct = ^LaunchStruct;            { Launch record }
  57.   LaunchStruct = record
  58.                      pfName: ^Str255;         { pointer to application name }
  59.                    param: integer;          { alternate video,audio buffers }
  60.                    LC: packed array[0..1] of char; { extended parameters }
  61.                    extBlockLen: longint;           { extra block length }
  62.                    fFlags: integer;         { finder file info flags }
  63.                    launchFlags: longint;    { bits 30,31 = 1 for sublaunch }
  64.                  end;
  65.                                     
  66.   FileLInfo = record               { Finder files }
  67.                 vRefNum: integer;  { 2 bytes }
  68.                 fType: OSType;     { file type - 4 bytes }
  69.                 version: boolean;  { 1 byte } 
  70.                 unused: boolean;   { 1 byte }
  71.                 fName: Str255;     { 1 length byte, variable length string }
  72.               end;
  73.  
  74.   APHdl = ^APPtr;                  { Finder info }
  75.   APPtr = ^APRec;
  76.   APRec = record
  77.             message: integer;      { 2 bytes }
  78.             count: integer;        { 2 bytes }
  79.             files: array[1..1] of FileLInfo;
  80.           end;
  81.  
  82.   LaunchData = record              { application & document data settings }
  83.                  vName: Str255;    { volume name }
  84.                  vRefNum: integer; { working directory reference # }
  85.                  dirID: longint;   { directory ID # }
  86.                  fName: Str255;    { file name }
  87.                  fType: OSType;    { file type }
  88.                  useIt: integer;   { whether to open associated document }
  89.                end;
  90.  
  91. var
  92.   { file system parameter blocks }
  93.   myPB: ParamBlockRec;
  94.   myHPB: HParamBlockRec;
  95.   myInfoPB: CInfoPBRec;
  96.   myWDPB: WDPBRec;
  97.   { launch data records }
  98.   appLData: LaunchData;  { application }
  99.   docLData: LaunchData;  { document }
  100.   { miscellaneous }
  101.   done: integer;   { done flag }
  102.   lcnt: integer;   { loop counter }
  103.  
  104.  
  105. { ****** Utility functions and procedures ****** }
  106.  
  107. procedure AlertMsg(msgStr1, msgStr2:Str255);
  108.   { Procedure to print alert message. 
  109.     To print numerics, use something like this:
  110.       testStr1, testStr2: Str255;
  111.        NumToString(reqCnt, testStr1);
  112.       NumToString(reqCnt, testStr2);
  113.       AlertMsg(testStr1, testStr2); }
  114.   var
  115.     theRect:rect;
  116.     ignore:integer;
  117.   begin
  118.     ParamText(msgStr1, msgStr2, '', '');
  119.     ignore := StopAlert(StopDlogID,NIL);
  120.   end;   { procedure AlertMsg }
  121.  
  122. { *********      Error Handling     *********** }
  123.  
  124. function GetErrorMsg(result:OSErr):string;
  125.   begin
  126.     result := abs(result);
  127.     case result of
  128.       18: GetErrorMsg := 'driver error during status operation';
  129.       19: GetErrorMsg := 'driver error during read operation';
  130.       20: GetErrorMsg := 'driver error during write operation';
  131.       27: GetErrorMsg := 'driver I/O error caused abort of operation';
  132.       28: GetErrorMsg := 'driver not open';
  133.       33: GetErrorMsg := 'the file directory is full';
  134.       34: GetErrorMsg := 'all allocation blocks on the volume are full';
  135.       35: GetErrorMsg := 'the specified volume is not mounted';
  136.       36: GetErrorMsg := 'there was an unspecified I/O error';
  137.       37: GetErrorMsg := 'the file or volume name is bad';
  138.       39: GetErrorMsg := 'logical EOF reached unexpectedly';
  139.       40: GetErrorMsg := 'attempt made to position before start of file';
  140.       42: GetErrorMsg := 'too many files are open';
  141.       43: GetErrorMsg := 'the file could not be found';
  142.       44: GetErrorMsg := 'the volume is locked by hardware setting';
  143.       45: GetErrorMsg := 'the file is locked';
  144.       46: GetErrorMsg := 'the volume is locked by a software flag';
  145.       47: GetErrorMsg := 'the file is already in use';
  146.       48: GetErrorMsg := 'a file with specified name exists';
  147.       49: GetErrorMsg := 'the file is already open for read/write';
  148.       50: GetErrorMsg := 'no volume specified and no default volume';
  149.       51: GetErrorMsg := 'a non-existent path was specified';
  150.       52: GetErrorMsg := 'the was an error finding current position in file';
  151.       53: GetErrorMsg := 'the specified volume is not on-line';
  152.       54: GetErrorMsg := 'attempt to open a locked file for writing';
  153.       55: GetErrorMsg := 'attempt to mount an already mounted volume';
  154.       56: GetErrorMsg := 'the specified drive number is not mounted';
  155.       57: GetErrorMsg := 'the volume lacks a Macintosh format directory';
  156.       58: GetErrorMsg := 'there was an external file system error';
  157.       59: GetErrorMsg := 'there was a problem during renaming';
  158.       60: GetErrorMsg := 'the master directory block is bad';
  159.       61: GetErrorMsg := 'read/write permission does not allow writing';
  160.      108: GetErrorMsg := 'there is insufficient application memory';
  161.      109: GetErrorMsg := 'a nil master pointer has been encountered';
  162.      111: GetErrorMsg := 'attempt to operate on a free block';
  163.      112: GetErrorMsg := 'attempt to purge a locked block';
  164.      117: GetErrorMsg := 'the block is locked';
  165.      120: GetErrorMsg := 'the directory could not be found';
  166.      121: GetErrorMsg := 'too many working directories are open';
  167.      122: GetErrorMsg := 'a folder cannot be placed in its own subfolder';
  168.      123: GetErrorMsg := 'attempt HFS operations on non-HFS volume';
  169.      127: GetErrorMsg := 'there was an internal file system error';
  170.      128: GetErrorMsg := 'printing aborted at user request';
  171.     end;
  172.   end;   { function GetErrorMsg }
  173.   
  174. procedure IOCheck(result:OSErr);
  175.   var
  176.     ignore:integer;
  177.     errorString:Str255;
  178.   begin
  179.     if result <> NoErr then
  180.       begin
  181.         NumToString(result,errorString);
  182.         ParamText('Macintosh OS Error #:',
  183.                    errorString,
  184.                   'Macintosh OS Error Desc:',
  185.                    GetErrorMsg(result));
  186.         ignore := StopAlert(StopDlogID,NIL);
  187.       end
  188.   end;   { procedure IOCheck }
  189.          
  190. procedure LaunchFailed(errNo:OSErr);
  191.   var
  192.     ignore:integer;
  193.     errorString:Str255;
  194.   begin
  195.     NumToString(errNo,errorString);
  196.     ParamText('Launch Error #:',
  197.                errorString,
  198.               '','');
  199.     ignore := StopAlert(StopDlogID,NIL);
  200.   end;   { procedure LaunchFailed }
  201.          
  202. procedure NotFoundMsg(str1,str2:Str255);
  203.   var
  204.     ignore: integer;
  205.   begin
  206.     ParamText('Please help locate',
  207.                str1, str2,
  208.               'using the following dialog...');
  209.     ignore := StopAlert(StopDlogID,NIL);
  210.   end;   { procedure LaunchFailed }
  211.  
  212. { *********   Configuration routines  *********** }
  213.  
  214. function ReadDefaultData: integer;
  215.   { Open the launch data file and read the data necessary for
  216.     locating launch application and associated document (if any). }
  217.   var
  218.     theErr: OSErr;
  219.     myAppFile: AppFile;
  220.     myRefNum: integer;
  221.     theResult: integer;
  222.     appMsg: integer;
  223.     appCount: integer;
  224.   begin
  225.     theResult := 0;   { be optimistic }
  226.     CountAppFiles(appMsg, appCount);
  227.     if appCount < 1 then
  228.       begin
  229.         theResult := 1;
  230.         AlertMsg('Dispatcher must be launched','from a data document');
  231.         { could create a new data document file instead }
  232.       end
  233.     else
  234.       begin
  235.         GetAppFiles(1, myAppFile);
  236.         { open the file for reading }
  237.         myPB.ioCompletion := nil;
  238.         myPB.ioNamePtr := @myAppFile.fName;
  239.         myPB.ioVRefNum := myAppFile.vRefNum;
  240.         myPB.ioPermssn := fsRdWrPerm;         { read/write permission }
  241.         myPB.ioMisc := nil;                   { use volume buffer }
  242.         theErr := PBOpen(@myPB, false);
  243.         if theErr <> noErr then
  244.           begin
  245.             IOCheck(theErr); 
  246.             theResult := 1;
  247.           end
  248.         else
  249.           begin
  250.             myRefNum := myPB.ioRefNum;
  251.             { read the application launch data }
  252.             myPB.ioCompletion := nil;
  253.             myPB.ioRefNum := myRefNum;
  254.             myPB.ioBuffer := @appLData;
  255.             myPB.ioReqCount := sizeof(LaunchData);       
  256.             myPB.ioPosMode := fsAtMark;                   
  257.             myPB.ioPosOffset := 0;                   
  258.             theErr:= PBRead(@myPB, false);
  259.             if ((theErr <> noErr) or (myPB.ioActCount < 1)) then
  260.               begin
  261.                 appLData.vName := 'Uninitialized'; 
  262.                 appLData.dirID := 0; 
  263.                 appLData.fName := 'Uninitialized'; 
  264.                 appLData.fType := 'APPL'; 
  265.                 appLData.useIt := 1;    { not used }
  266.               end;
  267.         
  268.             { read the document launch data }
  269.             myPB.ioCompletion := nil;
  270.             myPB.ioRefNum := myRefNum;
  271.             myPB.ioBuffer := @docLData;
  272.             myPB.ioReqCount := sizeof(LaunchData);       
  273.             myPB.ioPosMode := fsAtMark;                   
  274.             myPB.ioPosOffset := 0;                   
  275.             theErr:= PBRead(@myPB, false);
  276.             if ((theErr <> noErr) or (myPB.ioActCount < 1)) then
  277.               begin
  278.                 docLData.vName := 'Uninitialized'; 
  279.                 docLData.dirID := 0; 
  280.                 docLData.fName := 'Uninitialized'; 
  281.                 docLData.fType := 'TEXT'; 
  282.                 docLData.useIt := 1; 
  283.               end;
  284.         
  285.             { close the data file }
  286.             myPB.ioCompletion := nil;
  287.             myPB.ioRefNum := myRefNum;
  288.             theErr:= PBClose(@myPB, false);
  289.           end;   { if data file opened OK }
  290.       end;   { if app files count is > 0 }
  291.     ReadDefaultData := theResult;
  292.   end;   { procedure ReadDefaultData }
  293.  
  294. function WriteDefaultData: integer;
  295.   { Open the launch data file and write the data necessary for
  296.     locating launch application and associated document (if any).
  297.     This should take place any time the data is changed. }
  298.   var
  299.     theErr: OSErr;
  300.     myAppFile: AppFile;
  301.     myRefNum: integer;
  302.     theResult: integer;
  303.   begin
  304.     theResult := 0;   { be optimistic }
  305.     GetAppFiles(1, myAppFile);
  306.     { open the file for reading/writing }
  307.     myPB.ioCompletion := nil;
  308.     myPB.ioNamePtr := @myAppFile.fName;
  309.     myPB.ioVRefNum := myAppFile.vRefNum;
  310.     myPB.ioPermssn := fsRdWrPerm;         { read/write permission }
  311.     myPB.ioMisc := nil;                   { use volume buffer }
  312.     theErr:= PBOpen(@myPB, false);
  313.     if theErr <> noErr then
  314.       begin
  315.         IOCheck(theErr); 
  316.         theResult := 1;
  317.       end
  318.     else
  319.       begin
  320.         myRefNum := myPB.ioRefNum;
  321.         { write the application launch data }
  322.         myPB.ioCompletion := nil;
  323.         myPB.ioRefNum := myRefNum;
  324.         myPB.ioBuffer := @appLData;
  325.         myPB.ioReqCount := sizeof(LaunchData);       
  326.         myPB.ioPosMode := fsAtMark;                   
  327.         myPB.ioPosOffset := 0;                   
  328.         theErr:= PBWrite(@myPB, false);
  329.         if ((theErr <> noErr) or (myPB.ioActCount < 1)) then
  330.           begin
  331.             IOCheck(theErr); 
  332.             theResult := 1;
  333.           end
  334.         else
  335.           begin
  336.             { write the document launch data }
  337.             myPB.ioCompletion := nil;
  338.             myPB.ioRefNum := myRefNum;
  339.             myPB.ioBuffer := @docLData;
  340.             myPB.ioReqCount := sizeof(LaunchData);       
  341.             myPB.ioPosMode := fsAtMark;                   
  342.             myPB.ioPosOffset := 0;                   
  343.             theErr:= PBWrite(@myPB, false);
  344.             if ((theErr <> noErr) or (myPB.ioActCount < 1)) then
  345.               begin
  346.                 IOCheck(theErr); 
  347.                 theResult := 1;
  348.               end;   { if bad document write }
  349.           end;   { if application write OK }
  350.         
  351.         { close the data file }
  352.         myPB.ioCompletion := nil;
  353.         myPB.ioRefNum := myRefNum;
  354.         theErr:= PBClose(@myPB, false);
  355.         
  356.       end;   { if data file opened OK }
  357.     WriteDefaultData := theResult;
  358.   end;   { procedure WriteDefaultData }
  359.  
  360. function FindApplFile: integer;
  361.   { Given the info in the data document, try to find the target
  362.     application's working directory reference number. If it can't
  363.     be found, then prompt the user to find it. }
  364.   var
  365.     theErr: OSErr;
  366.     theResult: integer;
  367.     reply: SFReply;
  368.     topLeft: Point;
  369.     fileFilter: SFTypeList;
  370.     appdone, appfound: integer;
  371.     theName: Str255;
  372.     theIndex: integer;
  373.     theRefNum: integer;
  374.   begin
  375.     theResult := 0;   { be optimistic }
  376.     { see if the volume is mounted }
  377.     theIndex := 1;
  378.     appdone := 0; appfound := 0; theName:= '';
  379.     repeat
  380.       myHPB.ioVolIndex := theIndex;       
  381.       myHPB.ioCompletion := NIL;
  382.       myHPB.ioNamePtr := @theName;
  383.       theErr := PBHGetVInfo(@myHPB, false);
  384.       if theErr <> noErr then
  385.         appdone := 1;   { no more volumes to check }
  386.       if theName = appLData.vName then
  387.         begin
  388.           appfound := 1; appdone := 1;
  389.           theRefNum := myHPB.ioVRefNum;
  390.         end;
  391.       theIndex := theIndex + 1;
  392.     until appdone > 0;
  393.     
  394.     if appfound = 0 then
  395.       theResult := 1
  396.     else
  397.       begin
  398.        { see if file can be found  }
  399.         myHPB.ioCompletion := NIL;
  400.         myHPB.ioNamePtr := @appLData.fName;
  401.         myHPB.ioVRefNum := theRefNum;         { from call above } 
  402.         myHPB.ioFDirIndex := 0;               { use file name, not index }
  403.         myHPB.ioDirID := appLData.dirID;      { use stored Dir ID }
  404.         theErr := PBHGetFInfo(@myHPB,false); 
  405.         if (theErr <> noErr) then
  406.           begin
  407.             { IOCheck(theErr); }
  408.             theResult := 1;
  409.           end
  410.         else
  411.           begin
  412.             { get the current application WD Ref Num }
  413.             myWDPB.ioCompletion := nil;
  414.             myWDPB.ioNamePtr := nil;   { directory name }
  415.             myWDPB.ioVRefNum := theRefNum;
  416.             myWDPB.ioWDProcID := longint('ERIK');
  417.             myWDPB.ioWDDirID := appLData.dirID;
  418.             theErr := PBOpenWD(@myWDPB, false);
  419.             appLData.vRefNum := myWDPB.ioVRefNum;
  420.             IOCheck(theErr);
  421.           end;   { getting the current WD Ref Num }
  422.       end;   { if volume was found OK }
  423.       
  424.     { if application couldn't be found using default information,
  425.       make the user find it manually. }
  426.     if theResult <> 0 then
  427.       begin
  428.         NotFoundMsg('the program file: ',appLData.fName);  
  429.         topLeft.h := 80; topLeft.v := 70;          
  430.         fileFilter[0] := appLData.fType;  
  431.         SFGetFile(topLeft,'',NIL,1,fileFilter,NIL,reply);
  432.         if reply.Good then
  433.           begin
  434.             theResult := 0;  { reset the error flag }
  435.             appLData.vRefNum := reply.vRefNum;  { WD RefNum }
  436.             appLData.fName := reply.fName;
  437.             appLData.fType := reply.fType;
  438.             { look up new volume name }
  439.             myHPB.ioCompletion := NIL;
  440.             myHPB.ioNamePtr := @appLData.vName;
  441.             myHPB.ioVRefNum := appLData.vRefNum;  
  442.             myHPB.ioVolIndex := 0;      { use vRefNum, not name }
  443.             theErr := PBHGetVInfo(@myHPB,false); 
  444.             IOCheck(theErr);
  445.             { look up new application directory ID }
  446.             myInfoPB.ioCompletion := nil;
  447.             myInfoPB.ioNamePtr:= @appLData.fName;
  448.             myInfoPB.ioVRefNum := appLData.vRefNum;
  449.             myInfoPB.ioFDirIndex := 0;
  450.             myInfoPB.ioDirID := 0;
  451.             theErr := PBGetCatInfo(@myInfoPB,false); 
  452.             IOCheck(theErr);
  453.             appLData.dirID := myInfoPB.ioFLParID;
  454.             
  455.             { update the default data }
  456.             if WriteDefaultData <> 0 then
  457.               theResult := 1;
  458.           end   { if reply is good }
  459.         else
  460.           begin
  461.             theResult := 1;
  462.           end;   { user cancelled application lookup }
  463.       end;   { if default application location not valid }
  464.     FindApplFile := theResult;
  465.   end;   { procedure FindApplFile }
  466.  
  467. function FindDocFile: integer;
  468.   { Given the info in the data document, try to find the launch
  469.     document's working directory reference number. If it can't
  470.     be found, then prompt the user to find it. }
  471.   var
  472.     theErr: OSErr;
  473.     theResult: integer;
  474.     reply: SFReply;
  475.     topLeft: Point;
  476.     fileFilter: SFTypeList;
  477.     docdone, docfound: integer;
  478.     theName: Str255;
  479.     theIndex: integer;
  480.     theRefNum: integer;
  481.   begin
  482.     theResult := 0;   { be optimistic }
  483.     
  484.     { see if there is an associated document with the application }
  485.     if (docLData.useIt = 1) then   { launch with associated document }
  486.       begin
  487.         { see if the volume is mounted }
  488.         theIndex := 1;
  489.         docdone := 0; docfound := 0; theName:= '';
  490.         repeat
  491.           myHPB.ioVolIndex := theIndex;       
  492.           myHPB.ioCompletion := NIL;
  493.           myHPB.ioNamePtr := @theName;
  494.           theErr := PBHGetVInfo(@myHPB, false);
  495.           if theErr <> noErr then
  496.             docdone := 1;   { no more volumes to check }
  497.           if theName = docLData.vName then
  498.             begin
  499.               docfound := 1; docdone := 1;
  500.               theRefNum := myHPB.ioVRefNum;
  501.             end;
  502.           theIndex := theIndex + 1;
  503.         until docdone > 0;
  504.     
  505.         if docfound = 0 then
  506.           theResult := 1
  507.         else
  508.           begin
  509.            { see if the file can be found in its directory }
  510.             myHPB.ioCompletion := NIL;
  511.             myHPB.ioNamePtr := @docLData.fName;
  512.             myHPB.ioVRefNum := theRefNum;        { from call above } 
  513.             myHPB.ioFDirIndex := 0;              { use file name, not index }
  514.             myHPB.ioDirID := docLData.dirID;     { use stored Dir ID }
  515.             theErr := PBHGetFInfo(@myHPB,false); 
  516.             if (theErr <> noErr) then
  517.               begin
  518.                 { IOCheck(theErr); }
  519.                 theResult := 1;
  520.               end
  521.             else
  522.               begin
  523.                 { get the current document WD Ref Num }
  524.                 myWDPB.ioCompletion := nil;
  525.                 myWDPB.ioNamePtr := nil;   { directory name }
  526.                 myWDPB.ioVRefNum := theRefNum;
  527.                 myWDPB.ioWDProcID := longint('ERIK');
  528.                 myWDPB.ioWDDirID := docLData.dirID;
  529.                 theErr := PBOpenWD(@myWDPB, false);
  530.                 docLData.vRefNum := myWDPB.ioVRefNum;
  531.                 IOCheck(theErr);
  532.               end;   { getting the current WD Ref Num }
  533.           end;   { if volume was found OK }
  534.       
  535.         { if document couldn't be found using default information,
  536.           make the user find it manually. }
  537.         if theResult <> 0 then
  538.           begin
  539.             NotFoundMsg('the associated document: ',docLData.fName); 
  540.             topLeft.h := 80; topLeft.v := 70;          
  541.             fileFilter[0] := docLData.fType;  
  542.             SFGetFile(topLeft,'',NIL,-1,fileFilter,NIL,reply);
  543.             if reply.Good then
  544.               begin
  545.                 theResult := 0;  { reset the error flag }
  546.                 docLData.vRefNum := reply.vRefNum;
  547.                 docLData.fName := reply.fName;
  548.                 docLData.fType := reply.fType;
  549.                 { look up new volume name }
  550.                 myHPB.ioCompletion := nil;
  551.                 docLData.vName := '';
  552.                 myHPB.ioNamePtr := @docLData.vName;
  553.                 myHPB.ioVRefNum := docLData.vRefNum;  
  554.                 myHPB.ioVolIndex := 0;      { use vRefNum, not name }
  555.                 theErr := PBHGetVInfo(@myHPB,false); 
  556.                 { look up new document parent directory ID }
  557.                 myInfoPB.ioCompletion := nil;
  558.                 myInfoPB.ioNamePtr:= @docLData.fName;
  559.                 myInfoPB.ioVRefNum := docLData.vRefNum;
  560.                 myInfoPB.ioFDirIndex := 0;
  561.                 myInfoPB.ioDirID := 0;
  562.                 theErr := PBGetCatInfo(@myInfoPB,false); 
  563.                 IOCheck(theErr);
  564.                 docLData.dirID := myInfoPB.ioFLParID;  { parent directory ID }
  565.             
  566.                 { update the default data }
  567.                 if WriteDefaultData <> 0 then
  568.                   theResult := 1;
  569.               end   { if reply is good }
  570.             else
  571.               begin
  572.                 theResult := 1;
  573.               end;   { user cancelled document lookup }
  574.           end;   { if default document location not valid }
  575.       end;   { if associated document exists }
  576.     FindDocFile := theResult;
  577.   end;   { procedure FindDocFile }
  578.  
  579. function EditDefaults: integer;
  580.   var
  581.     itemHit: integer;
  582.     itemType: integer;
  583.     itemRect: Rect;
  584.     dfltDlg: DialogPtr;
  585.     itemHdl1, itemHdl2: Handle;          { edittext handles }
  586.     itemHdl3: Handle;                    { check box dialog handle }
  587.     cBoxHdl1: ControlHandle;           { check box control handle }
  588.     theResult: integer;
  589.   begin
  590.      theResult := 0;
  591.     { get dialog & edittext handles }
  592.     dfltDlg := GetNewDialog(DfltDlogID, nil, Pointer(-1));
  593.     GetDItem(dfltDlg, 4, itemType, itemHdl1, itemRect);
  594.     GetDItem(dfltDlg, 5, itemType, itemHdl2, itemRect);
  595.     GetDItem(dfltDlg, 6, itemType, itemHdl3, itemRect);
  596.     cBoxHdl1 := ControlHandle(itemHdl3);
  597.     SetIText(itemHdl1, appLData.fName);     { app filename }
  598.     SetIText(itemHdl2, docLData.fName);     { doc filename }
  599.     SetCtlValue(cBoxHdl1, docLData.useIt);  { use/don't use document }
  600.     
  601.     { put up dialog }
  602.     itemHit := 0;
  603.     while ((itemHit > 3) or (itemHit < 1))  do
  604.       begin
  605.         ModalDialog(nil, itemHit);
  606.         if itemHit = 6 then                  { toggle the check box }
  607.           begin
  608.             if (GetCtlValue(cBoxHdl1) = 0) then            
  609.               SetCtlValue(cBoxHdl1, 1)        
  610.             else
  611.               SetCtlValue(cBoxHdl1, 0);      
  612.           end;
  613.       end;   { while }
  614.     { get dialog results }
  615.     case itemHit of    
  616.       1: begin                  { use names entered in dialog }
  617.            { application name }
  618.            GetIText(itemHdl1, appLData.fName);
  619.            { document name }
  620.            GetIText(itemHdl2, docLData.fName);
  621.          end;
  622.       2: begin                  { cause a search for new files }
  623.            appLData.fName := 'XXXXXXXX';
  624.            docLData.fName := 'XXXXXXXX';
  625.          end;
  626.       3: theResult := 1;        { cancel the launch }
  627.       end;
  628.     if (theResult = 0) then
  629.       begin
  630.         docLData.useIt := GetCtlValue(cBoxHdl1);
  631.         theResult := WriteDefaultData;  { update the defaults }
  632.       end;
  633.     DisposDialog(dfltDlg);
  634.     EditDefaults := theResult;
  635.   end;   { EditDefaults }
  636.  
  637. function TestCommandKey: integer;
  638.   { if command is held down while starting, the user will
  639.     be allowed to edit the default settings. The file data
  640.     has already been read at this point. If the user cancels,
  641.     the data defaults are not changed. }
  642.   var
  643.     theResult: integer;
  644.     eventReady: boolean;
  645.     myEvent: EventRecord;
  646.   begin
  647.     theResult := 0;
  648.     eventReady := GetNextEvent(everyEvent, myEvent);
  649.     if (BAND(myEvent.modifiers,cmdkey) > 0) then
  650.       theResult := EditDefaults;
  651.     TestCommandKey := theResult;  
  652.   end;   { TestCommandKey }
  653.  
  654.  
  655. { *********   Transfer and launch routines  *********** }
  656.  
  657. function ResetFinderInfo: integer;
  658.   { This is an example of how the finder parms could be changed
  659.     without allocating a new structure in the system heap. However,
  660.     the changes could not exceed the size of the existing heap object.
  661.     This routine is not used. }
  662.   var
  663.     fInfoHdl: Handle;
  664.     hSize: integer;
  665.     appParmRec: APRec;        { my Finder info }
  666.     theResult: integer;
  667.   begin
  668.     theResult := 0;   { be optimistic }
  669.     { get AppParmHandle, lock it, and copy parms record }
  670.     fInfoHdl := Handle(AppParmGVar);
  671.     fInfoHdl := Handle(fInfoHdl^);
  672.     HLock(fInfoHdl);
  673.     hSize := GetHandleSize(fInfoHdl);
  674.     BlockMove(Pointer(fInfoHdl^), Pointer(@appParmRec), hSize);
  675.     if (appParmRec.count > 0) then
  676.       begin
  677.         if (length(appParmRec.files[1].fName) >= length(docLData.fName)) then
  678.           begin    
  679.             { now change the launch parameters }
  680.             appParmRec.files[1].fName := docLData.fName;
  681.             appParmRec.files[1].fType := docLData.fType;
  682.             appParmRec.files[1].vRefNum := docLData.vRefNum;
  683.             BlockMove(Pointer(@appParmRec), Pointer(fInfoHdl^), hSize);
  684.           end
  685.       end
  686.     else 
  687.       begin
  688.         AlertMsg('Unable to reset', 'launch file parameters');
  689.         theResult := 1;
  690.       end;
  691.     HUnlock(fInfoHdl);
  692.     ResetFinderInfo := theResult;
  693.   end;   { ResetFinderInfo }
  694.  
  695. function ZeroFinderInfo: integer;
  696.   { If no associated documents are needed, then the number of
  697.     files in the app parm handle block must be set to zero, and
  698.     the file types set to zero so the Finder can clean up when
  699.     it gets control back. The number of files will always be 1
  700.     when this routine is called. }
  701.   var
  702.     fInfoHdl: Handle;         { Finder info handle }
  703.     hSize: integer;
  704.     appParmRec: APRec;        { my Finder info }
  705.     theResult: integer;
  706.   begin
  707.     theResult:= 0;   { be optimistic }
  708.     { get handle, lock it, and copy parms record }
  709.     fInfoHdl := Handle(AppParmGVar);
  710.     fInfoHdl := Handle(fInfoHdl^);
  711.     HLock(fInfoHdl);
  712.     hSize := GetHandleSize(fInfoHdl);
  713.     BlockMove(Pointer(fInfoHdl^), Pointer(@appParmRec), hSize);
  714.     if (appParmRec.count > 0) then
  715.       begin
  716.         { now change the launch parameters }
  717.         appParmRec.count := 0;
  718.         appParmRec.files[1].fType := OSType(longint(0));
  719.         { write it back out }
  720.         BlockMove(Pointer(@appParmRec), Pointer(fInfoHdl^), hSize);
  721.       end;
  722.     HUnlock(fInfoHdl);
  723.     ZeroFinderInfo := theResult;
  724.   end;   { ZeroFinderInfo }
  725.  
  726. function ReplaceFinderInfo: integer;
  727.   { To prepare for the launch, the finder launch information must
  728.     be reset to hold the name and type of the database startup file. 
  729.     This routine disposes the old AppParmHandle heap object and allocates
  730.     a new one on the system heap. Returns 0 if successful, 1 if an error
  731.     occurred. }
  732.   var
  733.     fInfoHdl: Handle;      { handle to Finder info }
  734.     sysZone: THz;          { system heap zone pointer }
  735.     appZone: THz;          { application heap zone pointer }
  736.     hSize: integer;
  737.     appParmRec: APRec;     { my Finder info }
  738.     theErr: OSErr;
  739.     theResult: integer;
  740.     saveAppParm: ^longint; { pointer used to access $AEC }
  741.   begin
  742.     theResult := 0;   { be optimistic }
  743.     { get handle, lock it, and copy parms record }
  744.     fInfoHdl := Handle(appParmGVar);
  745.     fInfoHdl := Handle(fInfoHdl^);
  746.     HLock(fInfoHdl);
  747.     hSize := GetHandleSize(fInfoHdl);
  748.     BlockMove(Pointer(fInfoHdl^), Pointer(@appParmRec), hSize);
  749.     
  750.     { dispose the old handle in the system zone}
  751.     HUnlock(fInfoHdl);
  752.     appZone := GetZone;
  753.     sysZone := SystemZone;
  754.     SetZone(sysZone);
  755.     DisposHandle(fInfoHdl);
  756.     
  757.     { allocate a new handle in the system zone }
  758.     hSize := 13 + length(docLData.fName);   { enough for one document }
  759.     fInfoHdl := NewHandle(hSize);
  760.     theErr := MemError;
  761.     SetZone(appZone);
  762.     if theErr <> noErr then
  763.       begin
  764.         IOCheck(theErr);
  765.         theResult := 1;
  766.       end
  767.     else
  768.       begin
  769.         HLock(fInfoHdl);
  770.         { put the new handle back at $AEC }
  771.         saveAppParm := Pointer(AppParmGVar);
  772.         saveAppParm^ := longint(fInfoHdl);
  773.     
  774.         { now change the launch parameters }
  775.         appParmRec.message:= 0;    { open it }
  776.         appParmRec.count:= 1;      { number of documents }
  777.         appParmRec.files[1].vRefNum := docLData.vRefNum;
  778.         appParmRec.files[1].fType := docLData.fType;
  779.         appParmRec.files[1].version := false;   
  780.         appParmRec.files[1].unused := false;
  781.         appParmRec.files[1].fName := docLData.fName;
  782.         BlockMove(Pointer(@appParmRec), Pointer(fInfoHdl^), hSize);
  783.         HUnlock(fInfoHdl);
  784.       end;   { if sys heap handle allocated OK }
  785.     
  786.     ReplaceFinderInfo := theResult;
  787.   end;   { ReplaceFinderInfo }
  788.  
  789. function LaunchIt(pLnch: pLaunchStruct): OSErr; 
  790. { from tech note #52 & 126 }
  791.   INLINE $205F, $A9F2, $3E80;
  792.  
  793. procedure DoLaunch;
  794. { Launch the application, given the information in the
  795.   launch data records for the application & document. }
  796.   var
  797.     fileInfo: FInfo;
  798.     ignore: integer;
  799.     myLaunch: LaunchStruct;
  800.     theErr: OSErr;
  801.     
  802.   begin
  803.     { set the default working directory to the application's folder }
  804.     myWDPB.ioCompletion := NIL;
  805.     myWDPB.ioNamePtr := NIL;
  806.     myWDPB.ioVRefNum := appLData.vRefNum;
  807.     theErr := PBSetVol(@myWDPB,false);  
  808.     
  809.     { get finder flags for launch record}
  810.     myInfoPB.ioNamePtr:= @appLData.fName;
  811.     myInfoPB.ioVRefNum := appLData.vRefNum;
  812.     myInfoPB.ioFDirIndex := 0;     { use the name instead of index }
  813.     myInfoPB.ioDirID := 0;
  814.     theErr := PBGetCatInfo(@myInfoPB,false); 
  815.         
  816.     { now launch the application }
  817.     myLaunch.pfName := @appLData.fName;    
  818.     myLaunch.param := 0; 
  819.     myLaunch.LC := 'LC';
  820.     myLaunch.extBlockLen := 6;
  821.     myLaunch.LaunchFlags:= $00000000;   { $C0000000 for sublaunch }
  822.     myLaunch.fFlags := myInfoPB.ioFlFndrInfo.fdFlags;  { from GetCatInfo }
  823.  
  824.     theErr := LaunchIt(@myLaunch);
  825.     if theErr < 0 then   
  826.       LaunchFailed(theErr);
  827.       
  828.   end;   { procedure DoLaunch }
  829.  
  830. function PrepareForLaunch: integer;
  831. { set up the AppParmsHandle data }
  832.   var
  833.     theResult: integer;
  834.   begin
  835.     theResult := 0;
  836.     if (docLData.useIt = 1) then  { application & document }
  837.       theResult := ReplaceFinderInfo
  838.     else                          { application only }
  839.       theResult := ZeroFinderInfo;
  840.     PrepareForLaunch := theResult;
  841.   end;   { function PrepareForLaunch }
  842.  
  843. procedure Initialize;
  844.   { initialize managers & globals }
  845.   begin
  846.     { initialize the managers }
  847.     InitGraf(@thePort);               
  848.     InitFonts;                        
  849.     InitWindows;                      
  850.     InitMenus;                        
  851.     TEInit;                           
  852.     InitDialogs(NIL);                 
  853.     FlushEvents(everyEvent,0);        
  854.   end; { Initialize }
  855.  
  856. { main program }
  857. begin 
  858.   Initialize;
  859.   lcnt := 1; done := 0;
  860.   while done = 0 do
  861.     begin
  862.       case lcnt of
  863.         1: done := ReadDefaultData;
  864.         2: done := TestCommandKey;
  865.         3: done := FindApplFile;
  866.         4: done := FindDocFile;
  867.         5: done := PrepareForLaunch;
  868.         6: DoLaunch;    { returns only if sublaunched }
  869.       end;
  870.       lcnt := lcnt + 1;
  871.       if lcnt > 6 then done := 1;
  872.     end;   { while }
  873.     
  874. end. { dispatcher }
  875.